package com.yugao.fintech.antelope.pay.client.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.yugao.fintech.antelope.base.security.utils.SecurityUtils;
import com.yugao.fintech.antelope.pay.PayApi;
import com.yugao.fintech.antelope.pay.client.assembly.ProductOrderAssembly;
import com.yugao.fintech.antelope.pay.client.constants.GoodsMqCons;
import com.yugao.fintech.antelope.pay.client.constants.OrderStatus;
import com.yugao.fintech.antelope.pay.client.constants.PayCacheKey;
import com.yugao.fintech.antelope.pay.client.manager.GoodsOrderManager;
import com.yugao.fintech.antelope.pay.client.manager.GoodsRefundManager;
import com.yugao.fintech.antelope.pay.client.mapper.GoodsOrderMapper;
import com.yugao.fintech.antelope.pay.client.model.entity.GoodsInfo;
import com.yugao.fintech.antelope.pay.client.model.entity.GoodsOrder;
import com.yugao.fintech.antelope.pay.client.model.entity.GoodsRefund;
import com.yugao.fintech.antelope.pay.client.model.mq.GoodsOrderCloseMQ;
import com.yugao.fintech.antelope.pay.client.model.mq.GoodsOrderRefundMQ;
import com.yugao.fintech.antelope.pay.client.model.rqrs.ConfirmOrderRQ;
import com.yugao.fintech.antelope.pay.client.model.rqrs.ConfirmOrderRS;
import com.yugao.fintech.antelope.pay.client.model.rqrs.GoodsRefundRQ;
import com.yugao.fintech.antelope.pay.client.service.GoodsOrderService;
import com.yugao.fintech.antelope.pay.config.PayClientProperties;
import com.yugao.fintech.antelope.pay.constant.PayWayCodeEnum;
import com.yugao.fintech.antelope.pay.model.entity.PayOrder;
import com.yugao.fintech.antelope.pay.model.entity.RefundOrder;
import com.yugao.fintech.antelope.pay.model.rqrs.pay.payorder.*;
import com.yugao.fintech.antelope.pay.model.rqrs.pay.refund.RefundOrderNotify;
import com.yugao.fintech.antelope.pay.model.rqrs.pay.refund.RefundOrderQO;
import com.yugao.fintech.antelope.pay.model.rqrs.pay.refund.RefundOrderRQ;
import com.yugao.fintech.antelope.pay.model.rqrs.pay.refund.RefundOrderRS;
import com.yugao.fintech.framework.assistant.utils.exception.Assert;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.assistant.sign.SignUtils;
import com.yugao.fintech.framework.cache.core.service.CacheService;
import com.yugao.fintech.framework.core.response.Resp;
import com.yugao.fintech.framework.core.response.RetOps;
import com.yugao.fintech.framework.lock.manager.ILockManager;
import com.yugao.fintech.framework.lock.model.ILock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class GoodsOrderServiceImpl extends ServiceImpl<GoodsOrderMapper, GoodsOrder> implements GoodsOrderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private PayApi payApi;
    @Autowired
    private GoodsOrderManager goodsOrderManager;
    @Autowired
    private PayClientProperties payClientProperties;
    @Autowired
    private GoodsRefundManager goodsRefundManager;
    @Autowired
    private ProductOrderAssembly productOrderAssembly;
    //    @Autowired
//    private IRefundInfoService refundInfoService;
    @Autowired
    private ILockManager lockManager;
    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ConfirmOrderRS confirmOrder(ConfirmOrderRQ req) {
        Long userId = SecurityUtils.getUserId();
        String mchOrderNo = String.valueOf(YitIdHelper.nextId());
        // 判断商品是否存在, 这里先mock
        GoodsInfo goodsInfo = GoodsInfo.builder()
                .id(req.getGoodsId()).title("test").detail("商品描述")
                .createdAt(new Date()).updatedAt(new Date()).img("https://www.baidu.com")
                .status("1")
                .nowAmount(new BigDecimal("0.01")).oldAmount(new BigDecimal("0.01"))
                .build();
        Assert.notNull(goodsInfo, new BizException("商品不存在"));

        // 检查订单价格
        this.checkPrice(goodsInfo, req);

        // 创建订单
        GoodsOrder order = goodsOrderManager.saveGoodsOrder(goodsInfo, req, mchOrderNo);

        
        Resp<UnifiedOrderRS> payResp = payApi.unifiedOrder(buildUnifiedOrderRQ(mchOrderNo, goodsInfo, req));
        RetOps.of(payResp).assertSuccess((r) -> new BizException(r.getMsg()));

        UnifiedOrderRS payRespData = payResp.getData();
        String orderStatus = payRespData.getOrderStatus();
        if (StringUtils.equals(orderStatus, PayOrder.STATE_FAIL)) {
            throw new BizException("订单创建失败, {}", payRespData.getErrMsg());
        }

        // 发送延迟消息, 关闭超时未支付的订单
        GoodsOrderCloseMQ msg = GoodsOrderCloseMQ.build(mchOrderNo, userId);
        rabbitTemplate.convertAndSend(GoodsMqCons.ORDER_EVENT_EXCHANGE, GoodsOrderCloseMQ.DELAY_ROUTING_KEY, msg.toMessage(), correlationData -> {
            correlationData.getMessageProperties().setDelay(GoodsOrderCloseMQ.TTL);
            return correlationData;
        });
        return ConfirmOrderRS.builder()
                .payDataType(payRespData.getPayDataType())
                .payData(payRespData.getPayData())
                .mchOrderNo(mchOrderNo).build();
    }
    
    public UnifiedOrderRQ buildUnifiedOrderRQ(String mchOrderNo, GoodsInfo goodsInfo, ConfirmOrderRQ req) {
        UnifiedOrderRQ bizRQ = new UnifiedOrderRQ();
        bizRQ.setMchOrderNo(mchOrderNo);
        bizRQ.setWayCode(PayWayCodeEnum.ofByCode(req.getWayCode()).getCode());
        bizRQ.setGoodsBody(goodsInfo.getDetail());
        bizRQ.setGoodsSubject(goodsInfo.getTitle());
        bizRQ.setGoodsSubject(goodsInfo.getTitle());
        bizRQ.setAmount(goodsInfo.getNowAmount());
        bizRQ.setTradeContent(JSON.toJSONString(goodsInfo));
        bizRQ.setTradeType("goods");
        bizRQ.setCurrency("cny");
        return bizRQ;
    }

    @Override
    public ConfirmOrderRS continuePay(String mchOrderNo) {
        Long userId = SecurityUtils.getUserId();;
        // 校验订单
        GoodsOrder order = goodsOrderManager.getByMchOrderNo(mchOrderNo, userId);
        if (Objects.isNull(order)) {
            log.warn("订单不存在, mchOrderNo: {}", mchOrderNo);
            throw new BizException("订单不存在");
        }
        if (order.getStatus().equalsIgnoreCase(OrderStatus.SUCCESS.getCode()) ||
                order.getStatus().equalsIgnoreCase(OrderStatus.REFUND_SUCCESS.getCode()) ||
                order.getStatus().equalsIgnoreCase(OrderStatus.REFUND_PROCESSING.getCode()) ) {
            log.info("订单已经支付, mchOrderNo: {}", mchOrderNo);
            throw new BizException("订单已经支付");
        }

        // 获取商品并判断商品是否存在
        GoodsInfo productInfo = GoodsInfo.builder()
                .id(order.getGoodsId()).title("test")
                .nowAmount(new BigDecimal("1")).oldAmount(new BigDecimal("1"))
                .build();
        Assert.notNull(productInfo, new BizException("商品不存在"));

        ContinuePayRQ continuePayRQ = ContinuePayRQ.builder().mchOrderNo(mchOrderNo).build();

        // 订单未支付, 调用第三方支付平台获取支付信息, 引导用户去支付
        Resp<UnifiedOrderRS> payResp = payApi.continuePay(continuePayRQ);
        RetOps.of(payResp).assertSuccess((r) -> new BizException("统一支付失败-{}", payResp.getMsg()));

        UnifiedOrderRS payRespData = payResp.getData();
        if (!StringUtils.equals(PayOrder.STATE_ING, payRespData.getOrderStatus())) {
//            order.setStatus(productOrderAssembly.toOrderStatus(unifiedOrderResp.getOrderStatus()).getCode());
            throw new BizException("非法订单状态");
        }

        return ConfirmOrderRS.builder().payData(payRespData.getPayData()).payDataType(payRespData.getPayDataType())
                .mchOrderNo(mchOrderNo).build();
    }

    /**
     * 验证价格
     */
    private void checkPrice(GoodsInfo productInfo, ConfirmOrderRQ req) {
        BigDecimal total = BigDecimal.valueOf(req.getBuyNum()).multiply(productInfo.getNowAmount());
        if (total.compareTo(req.getPayAmount()) != 0) {
            log.error("前端和后端的验证总价不一致");
            throw new BizException("价格验证失败");
        }
    }

    @Override
    public boolean closeGoodsOrder(GoodsOrderCloseMQ.MsgPayload payload) {
        String mchOrderNo = payload.getMchOrderNo();
        Long userId = payload.getUserId();

        /* 校验 */
        GoodsOrder order = goodsOrderManager.getByMchOrderNo(mchOrderNo, userId);
        if (Objects.isNull(order)) {
            log.warn("订单不存在, 取消本次关闭订单, req: {}", JSON.toJSONString(payload));
            return true;
        }
        if (order.getStatus().equalsIgnoreCase(OrderStatus.SUCCESS.getCode())) {
            log.info("订单已经支付, 取消本次关闭订单, req: {}", JSON.toJSONString(payload));
            return true;
        }

        /*
         * 步骤
         * 1. 如果订单未创建, 则更新商户端订单状态
         * 2. 如果订单已支付, 则更新商户端订单状态, 并记录支付日志
         * 3, 如果订单未支付, 则调用关单接口关闭订单, 并更新商户端订单状态
         */

        /* 未支付, 需要向第三方支付平台查询状态 */
        if (order.getStatus().equalsIgnoreCase(OrderStatus.NO_PAY.getCode())) {
            // 向支付中心查询状态
            PayOrderQO queryReq = new PayOrderQO();
            queryReq.setMchOrderNo(order.getMchOrderNo());
            Resp<PayOrderRS> queryResp = payApi.getOrder(queryReq);
            if (!RetOps.of(queryResp).isSuccess()) {
                throw new BizException("订单查询失败, msg: {}", queryResp.getMsg());
            }
            PayOrderRS queryData = queryResp.getData();

            // 判断订单是否存在
            if (PayOrder.STATE_NO_EXIST.equals(queryData.getStatus())) {
                goodsOrderManager.updateNoPayToClose(mchOrderNo, userId);
                log.info("支付平台未创建订单, 本地直接取消订单, req: {}", payload);
                return true;
            }

            if (PayOrder.STATE_REFUND.equals(queryData.getStatus())) {
                log.info("订单已经退款, 本地更新状态为退款, req: {}", payload);
                return true;
            }

            // 未支付成功
            if (!PayOrder.STATE_SUCCESS.equals(queryData.getStatus())) {
                // 未支付成功, 本地取消订单
                goodsOrderManager.updateNoPayToClose(mchOrderNo, userId);
                log.info("未支付成功, 本地取消订单, mchOrderNo: {}", payload.getMchOrderNo());
                // 调用支付平台进行关单操作
                ClosePayOrderRQ closePayOrderRQ = new ClosePayOrderRQ();
                closePayOrderRQ.setMchOrderNo(mchOrderNo);
                Resp<ClosePayOrderRS> bizRS = payApi.closePayOrder(closePayOrderRQ);
                if (!RetOps.of(bizRS).isSuccess()) {
                    log.error("errorMsg: {}", bizRS.getMsg());
                    throw new BizException("关单失败-{}", bizRS.getMsg());
                }
            } else {
                // 支付成功, 主动把订单状态改成已支付
                log.warn("支付成功, 但是统一支付平台回调通知失败, mchOrderNo: {}", payload.getMchOrderNo());
                goodsOrderManager.updateNoPayToSuccess(mchOrderNo, userId);

                // TODO 触发支付成功后的逻辑
            }
        }
        return true;
    }

    @Override
    public void payNotify(PayOrderNotify req) {
        SignUtils.checkSign(req.buildSignPayload(), false, payClientProperties.getSecretKey(), req);
        ILock lock = lockManager.getLock(PayCacheKey.PAY_GLOBAL_LOCK.formatKey(req.getPayOrderNo()));
        try {
            // 反向查询交易数据, 这里只是演示一下, 没有用到
            TradeDataQO tradeDataQO = new TradeDataQO();
            tradeDataQO.setMchOrderNo(req.getMchOrderNo());
            Resp<TradeDataRS> tradeData = payApi.getTradeData(tradeDataQO);
            log.info("tradeData: {}", Optional.ofNullable(tradeData.getData()).orElse(new TradeDataRS()).getTradeContent());

            boolean success = lock.tryLock(5L, TimeUnit.SECONDS);
            Assert.isTrue(success, new BizException("正在处理业务, 请稍后再通知"));
            String mchOrderNo = req.getMchOrderNo();

            /* 检验订单是否被支付过以及是否存在 */
            GoodsOrder order = goodsOrderManager.getByMchOrderNo(mchOrderNo, null);
            if (Objects.isNull(order)) {
                log.warn("订单不存在, req: {}", JSON.toJSONString(req));
                return;
            }
            if (!order.getStatus().equalsIgnoreCase(OrderStatus.NO_PAY.getCode())) {
                log.info("订单不处于未支付状态, 不做任何处理, req: {}", JSON.toJSONString(req));
                return;
            }

            /*  需要校验实际支付的金额是否一致 */
            if (order.getPayAmount().compareTo(req.getPayAmount()) != 0) {
                throw new BizException("金额校验失败");
            }

            /* 更新订单状态 */
            goodsOrderManager.updateNoPayToSuccess(mchOrderNo, null);
        } finally {
            lock.unlock();
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundNotify(RefundOrderNotify req) {
        SignUtils.checkSign(req.buildSignPayload(), false, payClientProperties.getSecretKey(), req);
        ILock lock = lockManager.getLock(PayCacheKey.PAY_GLOBAL_LOCK.formatKey(req.getMchRefundNo()));
        try {
            boolean success = lock.tryLock(5L, TimeUnit.SECONDS);
            Assert.isTrue(success, new BizException("正在处理退款业务, 请稍后再通知"));

            /* 检验订单是否处于退款中状态 */
            String mchRefundNo = req.getMchRefundNo();
            GoodsOrder order = goodsOrderManager.getByMchOrderNo(req.getMchOrderNo(), null);
            if (Objects.isNull(order)) {
                log.warn("退款的订单不存在, mchRefundNo: {}", mchRefundNo);
                return;
            }
            if (!OrderStatus.REFUND_PROCESSING.getCode().equals(order.getStatus())) {
                log.info("mchRefundNo: {}, status is not refund_processing", mchRefundNo);
                return;
            }

            /* 更新订单状态 */
            OrderStatus orderStatus = toOrderStatus(req);
            goodsOrderManager.updateRefundingToOther(mchRefundNo, null, orderStatus);

            GoodsRefund goodsRefund = goodsRefundManager.getOne(GoodsRefund.lqw().eq(GoodsRefund::getMchRefundNo, mchRefundNo));
            if (Objects.isNull(goodsRefund)) {
                log.error("退款订单不存在, mchRefundNo: {}", mchRefundNo);
                return;
            }

            if (!RefundOrder.STATE_ING.equals(goodsRefund.getStatus())) {
                log.error("退款状态不为退款中, mchOrderNo: {}, now status: {}", goodsRefund.getMchOrderNo(), goodsRefund.getStatus());
                return;
            }

            GoodsRefund updateGoodsRefund = GoodsRefund.builder().goodsId(order.getGoodsId())
                    .currency("cny")
                    .wayCode(req.getWayCode()).status(req.getStatus()).status(req.getStatus())
                    .successTime(new Date()).build();

            /* 更新退款订单 */
            goodsRefundManager.updateIngToOther(mchRefundNo, updateGoodsRefund);
        } finally {
            lock.unlock();
        }
    }

    private OrderStatus toOrderStatus(RefundOrderNotify req) {
        if (RefundOrder.STATE_CLOSED.equals(req.getStatus())) {
            return OrderStatus.CLOSED;
        }

        if (RefundOrder.STATE_ING.equals(req.getStatus())) {
            return OrderStatus.REFUND_PROCESSING;
        }

        if (RefundOrder.STATE_FAIL.equals(req.getStatus())) {
            return OrderStatus.REFUND_ABNORMAL;
        }

        if (RefundOrder.STATE_SUCCESS.equals(req.getStatus())) {
            return OrderStatus.REFUND_SUCCESS;
        }

        throw new BizException("未知订单状态-status: {}", req.getStatus());
    }

    @Override
    @Transactional
    public void closeGoodsOrder(String mchOrderNo) {
        Long userId = SecurityUtils.getUserId();
        GoodsOrder goodsOrder = goodsOrderManager.getByMchOrderNo(mchOrderNo, userId);
        if (Objects.isNull(goodsOrder)) {
            throw new BizException("订单不存在");
        }

        if (!OrderStatus.NO_PAY.getCode().equals(goodsOrder.getStatus())) {
            throw new BizException("订单状态异常");
        }

        /* 更新订单状态 */
        goodsOrderManager.updateNoPayToCancel(mchOrderNo, userId);

        /* 调用第三方平台取消订单 */
        ClosePayOrderRQ closePayOrderRQ = new ClosePayOrderRQ();
        closePayOrderRQ.setMchOrderNo(mchOrderNo);
        Resp<ClosePayOrderRS> closePayOrderRS = payApi.closePayOrder(closePayOrderRQ);
        if (!RetOps.of(closePayOrderRS).isSuccess()) {
            throw new BizException("关闭失败-{}", closePayOrderRS.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(GoodsRefundRQ req) {
        Long userId = SecurityUtils.getUserId();
        GoodsOrder order = goodsOrderManager.getByMchOrderNo(req.getMchOrderNo(), userId);
        Assert.notNull(order, "订单不存在");

        if (!OrderStatus.SUCCESS.getCode().equals(order.getStatus())) {
            throw new BizException("订单状态异常");
        }

        /* 更新订单状态为退款中 */
        goodsOrderManager.updateSuccessToRefunding(req.getMchOrderNo(), userId);

        /* 创建退款订单 */
        GoodsRefund goodsRefund = goodsRefundManager.saveGoodsRefund(req, order);

        /* 调用第三方支付平台发出退款 */
        RefundOrderRQ refundOrderReq = new RefundOrderRQ();
        refundOrderReq.setMchOrderNo(goodsRefund.getMchOrderNo());
        refundOrderReq.setMchRefundNo(goodsRefund.getMchRefundNo());
        refundOrderReq.setRefundAmount(goodsRefund.getRefundAmount());
        refundOrderReq.setRefundReason(goodsRefund.getRefundReason());

        Resp<RefundOrderRS> refundOrderRS = payApi.refundOrder(refundOrderReq);
        if (!RetOps.of(refundOrderRS).isSuccess()) {
            throw new BizException("退款失败, {}", refundOrderRS.getMsg());
        }

        /* 异步退款, 发送延迟消息, 处理退款中的订单, 避免由于某些原因支付平台回调不成功 */
        GoodsOrderRefundMQ msg = GoodsOrderRefundMQ.build(goodsRefund, userId);
        rabbitTemplate.convertAndSend(GoodsMqCons.ORDER_EVENT_EXCHANGE, GoodsOrderRefundMQ.ROUTING_KEY, msg.toMessage(), correlationData -> {
            correlationData.getMessageProperties().setDelay(GoodsOrderRefundMQ.TTL);
            return correlationData;
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean refund(GoodsOrderRefundMQ.MsgPayload payload) {
        Long userId = payload.getUserId();

        /* 校验 */
        GoodsOrder goodsOrder = goodsOrderManager.getByMchOrderNo(payload.getMchOrderNo(), userId);
        if (Objects.isNull(goodsOrder)) {
            log.warn("订单不存在, 取消本次订单退款, req: {}", JSON.toJSONString(payload));
            return true;
        }

        GoodsRefund goodsRefund = goodsRefundManager.getOne(GoodsRefund.lqw().eq(GoodsRefund::getMchRefundNo, payload.getMchRefundNo()));
        if (Objects.isNull(goodsRefund)) {
            log.info("退款订单不存在, 取消本次订单退款, req: {}", JSON.toJSONString(payload));
            return true;
        }
        if (goodsRefund.getStatus().equalsIgnoreCase(RefundOrder.STATE_SUCCESS)) {
            log.info("订单已经退款, 取消本次订单退款, req: {}", JSON.toJSONString(payload));
            return true;
        }

        /* 查询第三方支付平台退款信息 */
        OrderStatus orderStatus = OrderStatus.of(goodsOrder.getStatus());
        if (OrderStatus.REFUND_ABNORMAL == orderStatus || OrderStatus.REFUND_PROCESSING == orderStatus) {
            // 主动获取退款信息
            RefundOrderQO queryReq = new RefundOrderQO();
            queryReq.setMchRefundNo(goodsRefund.getMchRefundNo());
            
            Resp<RefundOrderRS> refundOrderResp = payApi.getRefundOrder(queryReq);
            if (!RetOps.of(refundOrderResp).isSuccess()) {
                throw new BizException("订单退款失败, msg: {}", refundOrderResp.getMsg());
            }

            RefundOrderRS refundOrderRS = refundOrderResp.getData();
            String status = refundOrderRS.getStatus();

            // 更新退款
            GoodsRefund updateGoodsRefund = GoodsRefund.builder().status(status).build();
            goodsRefundManager.updateIngToOther(payload.getMchRefundNo(), updateGoodsRefund);

            // 更新订单
            OrderStatus updateOrderStatus = null;
            if (RefundOrder.STATE_SUCCESS.equals(status)) {
                updateOrderStatus = OrderStatus.REFUND_SUCCESS;
            } else if (RefundOrder.STATE_FAIL.equals(status) || RefundOrder.STATE_CLOSED.equals(status)) {
                updateOrderStatus = OrderStatus.REFUND_ABNORMAL;
            } else if (RefundOrder.STATE_ING.equals(status)){
                rabbitTemplate.convertAndSend(GoodsMqCons.ORDER_EVENT_EXCHANGE, GoodsOrderRefundMQ.ROUTING_KEY, JSON.toJSONString(payload), correlationData -> {
                    correlationData.getMessageProperties().setDelay(GoodsOrderRefundMQ.TTL);
                    return correlationData;
                });
                return true;
            } else {
                throw new BizException("未知退款状态, status: {}, mchRefundNo: {}", status, payload.getMchRefundNo());
            }
            goodsOrderManager.updateRefundingToOther(payload.getMchOrderNo(), userId, updateOrderStatus);
        }
        return true;
    }
}
